package com.lsh.oms.worker.service.task;

import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.job.api.JobExecutionSingleShardingContext;
import com.dangdang.ddframe.job.internal.job.dataflow.AbstractBatchDataFlowElasticJob;
import com.lsh.base.trace.id.filter.TraceIdSetter;
import com.lsh.oms.api.model.es.OrderHeadForEs;
import com.lsh.oms.core.constant.Constants;
import com.lsh.oms.core.constant.EsConstants;
import com.lsh.oms.core.constant.LoggerConstant;
import com.lsh.oms.core.enums.ExceptionOrderStatus;
import com.lsh.oms.core.enums.OrderStatus;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.http.HttpMessage;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.proxy.service.BaseProxy;
import com.lsh.oms.core.proxy.service.EsProxy;
import com.lsh.oms.core.service.order.OrderQueryService;
import com.lsh.oms.core.utils.IdGenerator;
import com.lsh.oms.core.utils.LoggerUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * @author peter
 */
@Component
public class FilterExceptionOrderTaskDeal  extends AbstractBatchDataFlowElasticJob<List<Map<String, Object>>, JobExecutionSingleShardingContext>{

	private static Logger logger = LoggerFactory.getLogger(FilterExceptionOrderTaskDeal.class);

	@Autowired
	private OrderQueryService orderQueryService;

	@Autowired
	private EsProxy esProxy;

	@Value("${tms.isNotPay}")
	private String tmsIsNotPayUrl;

	@Value("${tms.likeWholeSale}")
	private String tmsLikeWholeSaleUrl;

	@Value("${filterException.logName}")
	private String filterExceptionLoggerName;

	private static final String EXCEPTION_MAP_ORDER_CODE_KEY = "orderCode";

	private static final String EXCEPTION_MAP_STATUS_KEY = "exceptionStatus";

	private static final String TMS_REQUEST_BODY = "orderIds";

	private static final Set<Integer> notShipSet;

	private static final Set<Integer> notReceiptSet;

	private static final Set<Integer> notPaySet;

	private static final Set<Integer> likeWholeSaleSet;

	private static Logger filterExceptionLogger;

	static {
		notShipSet = new HashSet<>();
		notShipSet.add(OrderStatus.ORDER_NO_EDIT.getIndex());
		notShipSet.add(OrderStatus.ORDER_COMMITING_OFC.getIndex());
		notShipSet.add(OrderStatus.ORDER_COMMITED_OFC.getIndex());
		notShipSet.add(OrderStatus.ORDER_WM_NEW.getIndex());

		notReceiptSet = new HashSet<>();
		notReceiptSet.add(OrderStatus.ORDER_DELIVERY.getIndex());

		notPaySet = new HashSet<>();
		notPaySet.add(OrderStatus.ORDER_SIGN.getIndex());

		likeWholeSaleSet = new HashSet<>();
		likeWholeSaleSet.add(OrderStatus.ORDER_SIGN.getIndex());
	}

	@PostConstruct
	public void init() {
		filterExceptionLogger = LoggerFactory.getLogger(filterExceptionLoggerName);
	}

	@Override
	public List<List<Map<String, Object>>> fetchData(JobExecutionSingleShardingContext context) {
		TraceIdSetter.set();
		String uuid = UUID.randomUUID().toString();
		String prefix =  "[FilterExceptionOrderTaskDeal][" + uuid +"]";

		LoggerUtils.saveLog(prefix + "[Select filter exception order task start]",
				LoggerConstant.LOGGER_INFO);

		Map<Long, Integer> exceptionMap = getEsExceptionList();

		if(null == exceptionMap || exceptionMap.size() == 0){

			return Collections.EMPTY_LIST;
		}

//		filterExceptionLogger.info(prefix + "[Es exception order size:" + exceptionMap.size()
//				+ "][Es exception order list:" + JSON.toJSONString(exceptionMap) + "]");

		List<OrderHead> orderHeadList = orderQueryService.findListByStatusRange(OrderStatus.ORDER_NO_EDIT.getIndex(), OrderStatus.ORDER_WM_NEW.getIndex());
		orderHeadList.addAll(orderQueryService.findListByStatusRange(OrderStatus.ORDER_DELIVERY.getIndex(), OrderStatus.ORDER_DELIVERY.getIndex()));
//		filterExceptionLogger.info(prefix + "[Db maybe exception order size:" + orderHeadList.size()
//				+ "][Db maybe exception order list:" + JSON.toJSONString(orderHeadList) + "]");

		List<Map<String, Object>> orderHeadExceptionList = new ArrayList<>();
		for(OrderHead orderHead : orderHeadList) {
			//判断是否为KA用户
			if(isKAUser(orderHead.getAddressInfo(), prefix)) {
				continue;
			}

			if(isNotShip(exceptionMap, orderHead.getOrderCode(), orderHead.getOrderTime(),orderHead.getOrderStatus())) {
				Map<String, Object> orderHeadMap = new HashMap<>();
				orderHeadMap.put(EXCEPTION_MAP_ORDER_CODE_KEY, orderHead.getOrderCode());
				orderHeadMap.put(EXCEPTION_MAP_STATUS_KEY, ExceptionOrderStatus.NOT_SHIP.getStatus());
				orderHeadExceptionList.add(orderHeadMap);
				continue;
			}
			if(isNotReceipt(exceptionMap, orderHead.getOrderCode(), orderHead.getOrderTime(),orderHead.getOrderStatus())) {
				Map<String, Object> orderHeadMap = new HashMap<>();
				orderHeadMap.put(EXCEPTION_MAP_ORDER_CODE_KEY, orderHead.getOrderCode());
				orderHeadMap.put(EXCEPTION_MAP_STATUS_KEY, ExceptionOrderStatus.NOT_RECEIPT.getStatus());
				orderHeadExceptionList.add(orderHeadMap);
				continue;
			}
		}
//		filterExceptionLogger.info(" [FilterExceptionOrderTaskDeal][" + uuid + "][OrderHeadExceptionList:" + JSON.toJSONString(orderHeadExceptionList) + "]");

		//获取未到账列表
		Integer isNotPayTime = ExceptionOrderStatus.NOT_PAY.getTimeInterval();
		try {
			Long logSign = IdGenerator.genId();

			filterExceptionLogger.info(prefix + "[Get tms not pay, logSign:" + logSign + "]");

			HttpMessage httpMessageIsNotPay = new HttpMessage();
//					HttpUtils.sendPostRequest(tmsIsNotPayUrl, ToolService.getRequestTmsHeader(logSign));

			filterExceptionLogger.info(prefix + "[Get tms not pay return value:" + httpMessageIsNotPay.getMessage() + "]");

			JSONArray isNotPayArray = JSONArray.parseArray(BaseProxy.getContent(httpMessageIsNotPay.getMessage()));
			Iterator<Object> iterator = isNotPayArray.iterator();
			while (iterator.hasNext()) {
				Long value = Long.parseLong(String.valueOf(iterator.next()));

				OrderHead orderHead = orderQueryService.findByCode(value, false, false);
				if(orderHead == null) {
					continue;
				}
				//判断是否为KA用户
				if(isKAUser(orderHead.getAddressInfo(), prefix)) {
					continue;
				}

				Integer orderTime = orderHead.getOrderTime();
				if(orderTime == null) {
					continue;
				}
				if(orderTime.compareTo(isNotPayTime) > 0) {
					continue;
				}

				if(exceptionMap.containsKey(value)) {
					Boolean flag = false;
					Integer exceptionStatus = exceptionMap.get(value);
					if(!StringUtils.isBlank(String.valueOf(exceptionStatus))) {
						if (exceptionStatus.compareTo(ExceptionOrderStatus.NOT_PAY.getStatus()) == 0){
							flag = true;
						}
					}
					exceptionMap.remove(value);
					if(flag) {
						continue;
					}
				}

				Map<String, Object> orderHeadMap = new HashMap<>();
				orderHeadMap.put(EXCEPTION_MAP_ORDER_CODE_KEY, value);
				orderHeadMap.put(EXCEPTION_MAP_STATUS_KEY, ExceptionOrderStatus.NOT_PAY.getStatus());

				orderHeadExceptionList.add(orderHeadMap);
			}
		} catch (BusinessException e) {
			LoggerUtils.saveLog(prefix + "[Get Tms Not Pay Is Fail][Error Class : " + e.getClass()
					+ ", Error Message : " + e.getMessage() + "]",
					LoggerConstant.LOGGER_ERROR);
			logger.error(e.getMessage(), e);
		} catch (Exception e) {
			LoggerUtils.saveLog(prefix + "[Get Tms Not Pay Is Fail][Error Class : " + e.getClass()
					+ ", Error Message : " + e.getMessage() + "]",
					LoggerConstant.LOGGER_ERROR);
			logger.error(e.getMessage(), e);
		}

		//获取疑似批市列表
		try {
			Long logSign = IdGenerator.genId();

			filterExceptionLogger.info(prefix + "[Get tms like whole sale, logSign:" + logSign + "]");

			HttpMessage httpMessageLikeWholeSale = new HttpMessage();
//					HttpUtils.sendPostRequest(tmsLikeWholeSaleUrl, ToolService.getRequestTmsHeader(logSign));

			filterExceptionLogger.info(prefix + "[Get tms like whole sale return value:" + httpMessageLikeWholeSale.getMessage() + "]");

			JSONArray listWholeSaleArray = JSONArray.parseArray(BaseProxy.getContent(httpMessageLikeWholeSale.getMessage()));
			Iterator<Object> iterator = listWholeSaleArray.iterator();
			while (iterator.hasNext()) {
				Long value = Long.parseLong(String.valueOf(iterator.next()));

				OrderHead orderHead = orderQueryService.findByCode(value, false, false);
				if(orderHead == null) {
					continue;
				}
				//判断是否为KA用户
				if(isKAUser(orderHead.getAddressInfo(), prefix)) {
					continue;
				}

				if(exceptionMap.containsKey(value)) {
					Boolean flag = false;
					Integer exceptionStatus = exceptionMap.get(value);
					if(!StringUtils.isBlank(String.valueOf(exceptionStatus))) {
						if (exceptionStatus.compareTo(ExceptionOrderStatus.LIKE_WHOLE_SALE.getStatus()) == 0){
							flag = true;
						}
					}
					exceptionMap.remove(value);
					if(flag) {
						continue;
					}
				}

				Map<String, Object> orderHeadMap = new HashMap<>();
				orderHeadMap.put(EXCEPTION_MAP_ORDER_CODE_KEY, value);
				orderHeadMap.put(EXCEPTION_MAP_STATUS_KEY, ExceptionOrderStatus.LIKE_WHOLE_SALE.getStatus());

				orderHeadExceptionList.add(orderHeadMap);
			}
		} catch(BusinessException e) {
			LoggerUtils.saveLog(prefix + "[Get Tms LIKE WHOLE SALE Is Fail][Error Class : " + e.getClass()
					+ ", Error Message : " + e.getMessage() + "]",
					LoggerConstant.LOGGER_ERROR);
			logger.error(e.getMessage(), e);
		} catch(Exception e) {
			LoggerUtils.saveLog(prefix + "[Get Tms LIKE WHOLE SALE Is Fail][Error Class : " + e.getClass()
					+ ", Error Message : " + e.getMessage() + "]",
					LoggerConstant.LOGGER_ERROR);
			logger.error(e.getMessage(), e);
		}

//		filterExceptionLogger.info(" [FilterExceptionOrderTaskDeal][" + uuid + "][Es exception order list:" + JSON.toJSONString(exceptionMap) + "]");

		for(Map.Entry entry : exceptionMap.entrySet()) {
			Map<String, Object> orderHeadMap = new HashMap<>();
			orderHeadMap.put(EXCEPTION_MAP_ORDER_CODE_KEY, entry.getKey());
			orderHeadMap.put(EXCEPTION_MAP_STATUS_KEY, "");

			orderHeadExceptionList.add(orderHeadMap);
		}

//		filterExceptionLogger.info(" [FilterExceptionOrderTaskDeal][" + uuid + "][Es exception order list:" + JSON.toJSONString(exceptionMap) + "]");

		List<List<Map<String, Object>>> returnList = new ArrayList<>();
		int count = orderHeadExceptionList.size();
		int total = count / 1000;
		int remain = count % 1000;
		if(remain != 0) {
			total += 1;
		}
		for(int i=0; i<total; i++) {
			int step = 1000;
			if(i == total - 1) {
				if(remain != 0) {
					step = remain;
				}
			}
			returnList.add(orderHeadExceptionList.subList(i * 1000, i * 1000 + step));
		}

		filterExceptionLogger.info(prefix + "[Return order list:" + JSON.toJSONString(returnList) + "]");
		LoggerUtils.saveLog(prefix + "[Select filter exception order task end][Size:" + orderHeadExceptionList.size() + "]", LoggerConstant.LOGGER_INFO);

		return returnList;
	}

	@Override
	public int processData(JobExecutionSingleShardingContext context, List<List<Map<String, Object>>> list) {
		int cnt = 0;
		for (List<Map<String, Object>> sonList : list) {
			try {
				boolean b = this.processOne(sonList);
				if (b) {
					cnt++;
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		return cnt;
	}

	private boolean processOne(List<Map<String, Object>> list) {
		String uuid = UUID.randomUUID().toString();

		LoggerUtils.saveLog(" [FilterExceptionOrderTaskDeal][" + uuid + "][Execute filter exception order task deal start]", LoggerConstant.LOGGER_INFO);
		filterExceptionLogger.info(" [FilterExceptionOrderTaskDeal][" + uuid + "][Params:" + JSON.toJSONString(list) + "]");

		//ElasticSearch同步订单状态
		List<OrderHeadForEs> esList = new ArrayList<>();
		for(Map<String, Object> map : list) {
			OrderHeadForEs orderHeadForEs = new OrderHeadForEs();
			orderHeadForEs.setOrderCode(Long.valueOf(String.valueOf(map.get(EXCEPTION_MAP_ORDER_CODE_KEY))));
			Integer exceptionStatus = 0;
			if (!StringUtils.isBlank(String.valueOf(map.get(EXCEPTION_MAP_STATUS_KEY)))) {
				exceptionStatus = Integer.valueOf(String.valueOf(map.get(EXCEPTION_MAP_STATUS_KEY)));
			}
			orderHeadForEs.setExceptionStatus(exceptionStatus);

			esList.add(orderHeadForEs);
		}
		esProxy.batchEditIndex(esList, "");

		LoggerUtils.saveLog(" [FilterExceptionOrderTaskDeal][" + uuid + "][Execute filter exception order task deal end]"
				, LoggerConstant.LOGGER_INFO);
		return true;
	}

	private static boolean isKAUser(String addressInfoContent, String prefix) {
		boolean isKAUser = false;

		if(StringUtils.isBlank(addressInfoContent)) {
			return isKAUser;
		}

		JSONObject addressInfo = null;
		try {
			addressInfo = JSONObject.parseObject(addressInfoContent);
		} catch(JSONException e) {
			filterExceptionLogger.error(prefix + "[Convert to JSONObject error, addressInfo:" + addressInfoContent + "]" +
					"[Error class" + e.getClass() + ", Error message:" + e.getMessage() + "]");
			return isKAUser;
		}

		if(addressInfo.containsKey(Constants.ADDRESS_INFO_USER_TYPE)) {
			Object obj = addressInfo.get(Constants.ADDRESS_INFO_USER_TYPE);
			String userType = obj == null ? null : String.valueOf(obj);

			if(!StringUtils.isBlank(userType)) {
				if(StringUtils.isEquals(userType, AddressInfoUserType.KAUser.getUserType())) {
					isKAUser = true;
				}
			}
		}

		return isKAUser;
	}

	private static Boolean isNotShip(Map<Long, Integer> exceptionMap, Long orderCode, Integer orderTime, Integer orderStatus) {
		Boolean isRepeat = false;
		if(orderTime.compareTo(ExceptionOrderStatus.NOT_SHIP.getTimeInterval()) < 0
				&& notShipSet.contains(orderStatus)) {
			if(!exceptionMap.containsKey(orderCode)) {
				isRepeat = true;
			} else {
				if(exceptionMap.get(orderCode).compareTo(ExceptionOrderStatus.NOT_SHIP.getStatus()) != 0) {
					isRepeat = true;
				}
				exceptionMap.remove(orderCode);
			}
		}
		return isRepeat;
	}

	private static Boolean isNotReceipt(Map<Long, Integer> exceptionMap, Long orderCode, Integer orderTime, Integer orderStatus) {
		Boolean isRepeat = false;
		if(orderTime.compareTo(ExceptionOrderStatus.NOT_RECEIPT.getTimeInterval()) < 0
				&& notReceiptSet.contains(orderStatus)) {
			if(!exceptionMap.containsKey(orderCode)) {
				isRepeat = true;
			} else {
				if(exceptionMap.get(orderCode).compareTo(ExceptionOrderStatus.NOT_RECEIPT.getStatus()) != 0) {
					isRepeat = true;
				}
				exceptionMap.remove(orderCode);
			}
		}
		return isRepeat;
	}

	private Map<Long, Integer> getEsExceptionList() {
		Map<Long, Integer> exceptionMap = new HashMap<>();

		String from = "0";
		String size = "1";

		size = esProxy.selectIndex(getEsExceptionQuery(from, size), true);

		if(size != null) {
			int temp = Integer.parseInt(size);
			if(temp > EsConstants.MAX_ES_NUMBER) {
				size = String.valueOf(EsConstants.MAX_ES_NUMBER);
			}
		} else {
			size = String.valueOf(EsConstants.MAX_ES_NUMBER);
		}

		String content = esProxy.selectIndex(getEsExceptionQuery(from, size), false);
		if(content == null) {
			return exceptionMap;
		}

		JSONArray contentArray = JSONArray.parseArray(content);
		Iterator<Object> iterator = contentArray.iterator();
		while (iterator.hasNext()) {
			JSONObject jsonObject = (JSONObject) iterator.next();

			JSONObject sourceObject = JSON.parseObject(jsonObject.getString("_source"));
			exceptionMap.put(Long.valueOf(sourceObject.getString("order_id")), Integer.valueOf(sourceObject.getString("exception_status")));
		}

		return exceptionMap;
	}

	private static String getEsExceptionQuery(String from, String size) {
		StringBuffer sbf = new StringBuffer();
		sbf.append("{\"query\": {\"bool\": {\"should\": [");
		int flag = 1;
		for(ExceptionOrderStatus exceptionOrderStatus : ExceptionOrderStatus.values()) {
			sbf.append("{\"term\":{\"exception_status\":");
			sbf.append(exceptionOrderStatus.getStatus());
			sbf.append("}}");
			if(flag != ExceptionOrderStatus.values().length) {
				sbf.append(",");
			}
			flag++;
		}
		sbf.append("]}},\"from\":");
		sbf.append(from);
		sbf.append(",\"size\":");
		sbf.append(size);
		sbf.append(",\"sort\": { \"");
		sbf.append(EsConstants.ES_SORT_FIELD);
		sbf.append("\": { \"order\": \"desc\" }}");
		sbf.append("}");

		return sbf.toString();
	}

	private enum AddressInfoUserType {
		Normal("1"), Company("2"), KAUser("3");

		AddressInfoUserType(String userType) {
			this.userType = userType;
		}

		private String userType;

		public String getUserType() {
			return userType;
		}
	}

}
